#include "mainwindow.hpp"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //Create Main Toolbar
    ui->mainToolBar->addAction(ui->actionOpen);
    ui->mainToolBar->addSeparator();
    ui->mainToolBar->addAction(ui->actionTrace);
    ui->mainToolBar->addAction(ui->actionStart);
    ui->mainToolBar->addAction(ui->actionPause);
    ui->mainToolBar->addAction(ui->actionStop);

    pix = new Pix();

    //Get the Settings and initialize MainWindow
    appSettings = AppSettings::getInstance();
    this->restoreGeometry(appSettings->getMainWindowGeometry());
    this->restoreState(appSettings->getMainWindowState());

    //Set up the DockWidgetAreas
    this->setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
    this->setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
    this->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);

    //Generate the DockWidgets
    codeView = new CodeView(pix, this);
    codeView->setObjectName("CodeView");
    this->setCentralWidget(codeView);

    breakpointView = new Breakpoints(pix->getProgramManagement(), this);
    breakpointView->setObjectName("Breakpoints");
    this->restoreDockWidget(breakpointView);
    ui->menuWindows->addAction(breakpointView->toggleViewAction());

    papView = new PapView(pix, this);
    papView->setObjectName("PAP View");
    this->restoreDockWidget(papView);
    ui->menuWindows->addAction(papView->toggleViewAction());

    registerView = new RegisterView(this);
    registerView->setObjectName("RegisterView");
    this->restoreDockWidget(registerView);
    ui->menuWindows->addAction(registerView->toggleViewAction());

    stackView = new StackView(this);
    stackView->setObjectName("StackView");
    this->restoreDockWidget(stackView);
    ui->menuWindows->addAction(stackView->toggleViewAction());

    watchList = new WatchList(pix->getDataMemoryOrganisation(), this);
    watchList->setObjectName("WatchList");
    this->restoreDockWidget(watchList);
    ui->menuWindows->addAction(watchList->toggleViewAction());

    this->msgBoxWatchdogTimeout = new QMessageBox(QMessageBox::Critical, "Watchdog Timeout", "Watchdog Timeout", QMessageBox::Ok, this);

    // Connect Signals to GUI
    connect(pix->getDataMemoryOrganisation(), SIGNAL(registerChanged(Register*)), registerView, SLOT(onRegisterChanged(Register*)));
    connect(pix->getDataMemoryOrganisation(), SIGNAL(switchBankSignal(bool)), registerView, SLOT(onBankChanged(bool)));

    connect(pix->getDecoder(), SIGNAL(customHighlightingRule(QString, int)), codeView, SLOT(onCustomHighlightingRule(QString, int)));
    connect(pix->getDecoder(), SIGNAL(customTooltip(QString,QString)), codeView, SLOT(onCustomTooltip(QString,QString)));
    connect(pix->getDecoder(), SIGNAL(variableFound(QString,QString)), watchList, SLOT(onVariableAdded(QString,QString)));

    connect(pix->getProgramManagement(), SIGNAL(breakPointToggled(int,bool)), codeView, SLOT(onBreakpointToggled(int,bool)));
    connect(pix->getProgramManagement(), SIGNAL(breakPointToggled(int,bool)), breakpointView, SLOT(onBreakpointToggled(int,bool)));

    connect(pix->getStackManagement(), SIGNAL(pushStack(int,int)), stackView, SLOT(onPushStack(int,int)));
    connect(pix->getStackManagement(), SIGNAL(popStack()), stackView, SLOT(onPopStack()));
    connect(pix->getStackManagement(), SIGNAL(stackOverflow()), stackView, SLOT(onStackOverflow()));

    connect(pix, SIGNAL(programCounterChanged(int)), registerView, SLOT(onProgramCounterChanged(int)));
    connect(pix, SIGNAL(programCounterChanged(int)), codeView, SLOT(onProgramCounterChanged(int)));
    connect(pix, SIGNAL(programCounterChanged(int)), this, SLOT(updateStatusbar()));
    connect(pix, SIGNAL(programCounterChanged(int)), papView, SLOT(onProgramCounterChanged(int)));
    connect(pix, SIGNAL(simulationStateChanged(bool,int)), codeView, SLOT(onSimulationStateChanged(bool,int)));

    connect(watchList, SIGNAL(variableTooltipChanged(QString,QString)), codeView, SLOT(onCustomTooltip(QString,QString)));
    connect(breakpointView, SIGNAL(jumpToBreakpoint(QString)), codeView, SLOT(onJumpToBreakpoint(QString)));
    connect(registerView, SIGNAL(requestChangeRegister(int,int)), pix->getDataMemoryOrganisation(), SLOT(onRequestChangeRegister(int,int)));
    connect(registerView, SIGNAL(requestChangeBank(bool)), pix->getDataMemoryOrganisation(), SLOT(onRequestSwitchBank(bool)));
    connect(codeView, SIGNAL(requestBreakpoint(int)), pix->getProgramManagement(), SLOT(onRequestBreakpoint(int)));


    // Create the Statusbar
    this->runtime = new QLabel(this->pix->getCPU()->getRuntimeAsString(), this);
    this->watchdog = new QLabel(this->pix->getWatchdog()->toString(), this);
    this->information = new QLabel("", this);
    ui->statusBar->addWidget(this->runtime, 12);
    ui->statusBar->addWidget(this->watchdog, 12);
    ui->statusBar->addWidget(this->information, 100);

    reset();
}

MainWindow::~MainWindow()
{
    AppSettings::releaseInstance();
    pix->stop();

    delete ui;
    delete codeView;
    delete breakpointView;
    delete papView;
    delete registerView;
    delete stackView;
    delete watchList;

    if (pix->isRunning())
    {
	pix->quit();
	if(!pix->wait(1000))
	{
	    qWarning() << "Quit does not work .. Using Terminate!";
	    pix->terminate();
	}
    }
    delete pix;
}

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}


void MainWindow::closeEvent(QCloseEvent *)
{
    appSettings->setMainWindowGeometry(this->saveGeometry());
    appSettings->setMainWindowState(this->saveState());
}

void MainWindow::on_actionOpen_triggered()
{
    QString filepath = QFileDialog::getOpenFileName( this, tr("Open"), QDir::currentPath(), tr("LST-Files (*.lst *.LST)"), 0, QFileDialog::DontUseNativeDialog );

    if (!filepath.isEmpty())
    {
	this->reset();
	pix->readCodeFile(filepath);
	codeView->addCommands(pix->getProgramManagement()->getCommandList());
	papView->reset();
	papView->createPap();
    }
}


void MainWindow::reset()
{
    //Reset to Defaults
    this->registerView->setHighlighting(false);
    this->codeView->reset();
    this->pix->reset();
    this->stackView->reset();
    this->watchList->reset();
    this->updateStatusbar();
    this->papView->reset();
    this->registerView->setHighlighting(true);
}


void MainWindow::resetToProgramStart()
{
    //Reset after Watchdog Timeout
    //this->pix->resetToProgramStart();
    //this->updateStatusbar();
}


void MainWindow::on_actionNew_triggered()
{
    this->reset();
}


void MainWindow::on_actionClose_triggered()
{
    qApp->quit();
}

void MainWindow::on_actionStart_triggered()
{
    this->pix->setSimulationMode(Pix::RUN);
    this->pix->start(QThread::IdlePriority);
}


void MainWindow::on_actionStop_triggered()
{
    this->pix->stop();
    this->papView->resetToProgramStart();
}


void MainWindow::on_actionTrace_triggered()
{
    this->pix->setSimulationMode(Pix::TRACE);
    this->pix->start(QThread::IdlePriority);
}


void MainWindow::on_actionPause_triggered()
{
    this->pix->pause();
}


void MainWindow::updateStatusbar()
{
    this->runtime->setText(pix->getCPU()->getRuntimeAsString());
    this->watchdog->setText(pix->getWatchdog()->toString());
}


void MainWindow::onWatchdogTimeout()
{
    this->pix->stop();
    int exitvalue = this->msgBoxWatchdogTimeout->exec();
    if(exitvalue == QMessageBox::Ok)
    {
	this->pix->resetToProgramStart();
    }
}


void MainWindow::on_actionFastMode_toggled(bool state)
{
    if (state)
    {
	// Disconnet not needed Signals to speed the GUI up
	disconnect(pix->getDataMemoryOrganisation(), SIGNAL(switchBankSignal(bool)), registerView, SLOT(onBankChanged(bool)));
	disconnect(pix->getStackManagement(), SIGNAL(stackOverflow()), stackView, SLOT(onStackOverflow()));
	//disconnect(watchList, SIGNAL(variableTooltipChanged(QString,QString)), codeView, SLOT(onCustomTooltip(QString,QString)));
	disconnect(breakpointView, SIGNAL(jumpToBreakpoint(QString)), codeView, SLOT(onJumpToBreakpoint(QString)));
	disconnect(registerView, SIGNAL(requestChangeRegister(int,bool,int)), pix->getDataMemoryOrganisation(), SLOT(onRequestChangeRegister(int,bool,int)));
	disconnect(pix, SIGNAL(programCounterChanged(int)), codeView, SLOT(onProgramCounterChanged(int)));

	this->registerView->setHighlighting(false);
	this->pix->setFastMode(true);
	this->information->setText("Fast Mode activated!");

    } else {
	// Reconnect all Signals to GUI
	connect(pix->getStackManagement(), SIGNAL(stackOverflow()), stackView, SLOT(onStackOverflow()));
	connect(pix->getDataMemoryOrganisation(), SIGNAL(switchBankSignal(bool)), registerView, SLOT(onBankChanged(bool)));
	//connect(watchList, SIGNAL(variableTooltipChanged(QString,QString)), codeView, SLOT(onCustomTooltip(QString,QString)));
	connect(breakpointView, SIGNAL(jumpToBreakpoint(QString)), codeView, SLOT(onJumpToBreakpoint(QString)));
	connect(registerView, SIGNAL(requestChangeRegister(int,bool,int)), pix->getDataMemoryOrganisation(), SLOT(onRequestChangeRegister(int,bool,int)));
	connect(pix, SIGNAL(programCounterChanged(int)), codeView, SLOT(onProgramCounterChanged(int)));

	this->registerView->setHighlighting(true);
	this->pix->setFastMode(false);
	this->information->clear();
    }
}
